package org.wejob.web;

import org.wejob.command.CommandRWQueue;
import org.wejob.web.api.QueueNode;
import org.wejob.web.buffer.SocketEmptyBufferQueue;
import org.wejob.web.buffer.SocketReadBufferQueue;
import org.wejob.web.buffer.SocketWriteBufferQueue;
import org.wejob.web.buffer.WebIOBuffer;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class WebClient implements Runnable, QueueNode {
    private Selector selector;
    private String serverIp;
    private int serverPort;
    private SocketChannel sc;
    private final SocketReadBufferQueue readBufferQueue;
    private final SocketWriteBufferQueue writeBufferQueue;
    private final SocketEmptyBufferQueue emptyBufferQueue;
    public final CommandRWQueue commandReadQueue;
    private final CommandRWQueue commandWriteQueue;

    private WebClient() throws IOException {
        this.selector = Selector.open();
        this.readBufferQueue = new SocketReadBufferQueue();
        this.emptyBufferQueue = new SocketEmptyBufferQueue();
        this.writeBufferQueue = new SocketWriteBufferQueue();
        this.commandReadQueue = new CommandRWQueue();
        this.commandWriteQueue = new CommandRWQueue();
    }

    public WebClient(SocketChannel sc) throws IOException {
        this();
        if (this.sc != null || this.sc.isConnected())
            throw new RuntimeException("server has connected!");
        this.initServerChannelWithObject(sc);
    }

    public WebClient(String IP, int port) throws IOException {
        this();
        this.createSocketChannelWittIPAddress(IP, port);
    }

    private void initServerChannelWithObject(SocketChannel socketChannel) throws IOException {
        this.sc = socketChannel;
        this.sc.configureBlocking(false);
        this.sc.register(this.selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
    }

    private void createSocketChannelWittIPAddress(String IP, int port) throws IOException {
        this.serverIp = IP;
        this.serverPort = port;
        InetSocketAddress isa = new InetSocketAddress(this.serverIp, this.serverPort);
        SocketChannel sc = SocketChannel.open(isa);
        //调用open静态方法创建连接到指定主机的SocketChannel
        this.initServerChannelWithObject(sc);
    }

    public SocketEmptyBufferQueue emptyBufferQueue() {
        return this.emptyBufferQueue;
    }

    public SocketReadBufferQueue readBufferQueue() {
        return this.readBufferQueue;
    }

    public SocketWriteBufferQueue writeBufferQueue() {
        return this.writeBufferQueue;
    }

    public void getKeySet() throws IOException {
        while (this.selector.select() > 0) {
            //轮询所有选择器接收到的操作
            messageHandler((selector.selectedKeys().iterator()));

        }
    }

    public void messageHandler(Iterator<SelectionKey> selectionKeyIte) throws IOException {
        while (selectionKeyIte.hasNext()) {
            SelectionKey key = selectionKeyIte.next();
            if (key.isWritable() && this.readBufferQueue.size() > 0) {
                this.sendMessage();
            }
            if (key.isReadable()) {
                this.acceptMessage();
            }
            selectionKeyIte.remove();
        }
    }

    private void acceptMessage() throws IOException {
        WebIOBuffer buffer = this.emptyBufferQueue.poll();
        this.sc.read(buffer.getVal());
        buffer.toRead();
        this.writeBufferQueue.offer(buffer);
    }

    //todo: doSend()
    private void sendMessage() throws IOException {
        WebIOBuffer buffer = this.readBufferQueue.poll();
        if (buffer == null)
            return;
        while (!buffer.isEmpty()) {
            this.sc.write(buffer.getVal());
        }
        buffer.toEmpty();
        this.emptyBufferQueue.offer(buffer);
    }

    private void hadler() {
        try {
            while (this.selector.select() > 0) {
                //轮询所有选择器接收到的操作
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> selectionKeyIte = selectionKeys.iterator();
                this.messageHandler(selectionKeyIte);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    public boolean connexted(){
        return this.sc.isConnected();
    }

    public void run() {
        this.hadler();
    }

    public static void main(String[] args) throws IOException {
        new Thread(new WebClient("localhost", 8888)).start();
    }

    @Override
    public QueueNode pre() {
        return null;
    }

    @Override
    public void setPre(QueueNode p) {

    }

    @Override
    public QueueNode next() {
        return null;
    }

    @Override
    public void setNext(QueueNode n) {
        this.setNext(n);
    }

    @Override
    public Object getVal() {
        return null;
    }

    @Override
    public void setVal(Object val) {

    }
}